#include "instruction.h"
#include <iostream>
//#include <sstream>
using namespace std;

/**
  * This function returns a string for conditional.
  * @param cond Condition to describe.
  * @return string description of the condition.
  */
static const char* getCondString(Instruction::Cond cond)
{
    switch((int)cond)
    {
        case Instruction::COND_ALWAYS:  return "    ";
        case Instruction::COND_EQUAL:   return "eq? ";
        case Instruction::COND_NEQUAL:  return "neq?";
        case Instruction::COND_LESS:    return "<   ";
        case Instruction::COND_GR_EQ:   return ">=  ";
        case Instruction::COND_GREATER: return ">   ";
        case Instruction::COND_L_EQ:    return "<=  ";
        case Instruction::COND_CARRY:   return "c?  ";
        case Instruction::COND_NCARRY:  return "nc? ";
        default: return "????";
    }
}

/**
  * This function converts Instruction to a textual representation.
  * @param out The stream, to output the result to.
  * @param inst Instruction to process.
  * @return out stream is returned.
  */
std::ostream& operator<<(std::ostream& out, const Instruction& instr)
{
    uint32_t word = instr.word;

    out << getCondString(instr.getCond()) << " ";

    // interpret claout + func
    Instruction::Function func = instr.getFunction();
    Instruction::AluOp aluOp = instr.getAluOp();
    static const char* aluStrings[] = {"ALU_UNKNOWN", "ALU_PASS_B",
            "ALU_NEG", "ALU_ADD", "ALU_SUB", "ALU_AND", "ALU_OR ",
            "ALU_NAND",
            "ALU_XOR",
            "ALU_NOT",
            "ALU_SHIFT_L",
            "ALU_SHIFT_R ",
            "ALU_SHIFT_A",
            "ALU_SWAP16",
            "ALU_SWAP8",
            "ALU_BSWAP"};

    switch(instr.getClass())
    {
        case Instruction::CLASS_ALU:
            out << "CLASS_ALU"
                << (instr.isWriteBackBitSet() ? "W " : "  ");

            if(aluOp <= sizeof(aluStrings))
                out << aluStrings[aluOp] << "\t";
            else
                out << "UNKNOWN    \t";
            break;

        case Instruction::CLASS_LDST:
            out << "CLASS_LDST ";
            if(func == Instruction::F_LDST_LOAD)
                out << "LOAD       \t";
            else if(func == Instruction::F_LDST_STORE)
                out << "STORE      \t";
            else
                out << '?';
            break;

        case Instruction::CLASS_JUMP:
            out << "CLASS_JUMP ";
            if(func == Instruction::F_JUMP_REL)
                out << "JMP RELATIV\t";
            else if(func == Instruction::F_JUMP_ABS)
                out << "JMP ABSOLUT\t";
            else
                out << "UNBEKANNT  \t";
            break;

        default:
            out << "CLASS_???  ";
    }

    // registers
    out << " r" <<  instr.getOpA() << '\t';


    if(not instr.labelB.empty())
        out << '\'' << instr.labelB << "\' ";
    else
        out << "\t";

    // B is immediate?
    if(instr.isImmB() == false) // if not, this is a register
        out << 'r' << instr.getOpB();
    else
        out << dec << ((int)15 - (int)instr.getOpB());
    out << '\t';

    // destination register
    out << " r" << instr.getDest();

    return out;
}
